#include "objsim.h"
#include <stdio.h>
#include <stdlib.h>

// Definição do nó da lista de funções:
typedef struct No
{
    // Campos para os tipos diferentes de funções:
    int(*funcao)(int);
    int(*funcao_param)(int,int);
    // Fim dos tipos de funções.
    int parametro;
    struct No *prox;
}no;

// Definição da CABEÇA da lista de funções:
typedef struct Cabeca
{
    struct No *inicio;
    struct No *fim;
}cabeca;

// Função para alocar um novo nó e preenchê-lo com valores padrões:
no *no_alocar()
{
    no *novo = (no*)malloc(sizeof(no));
    novo->funcao = NULL;
    novo->funcao_param = NULL;
    novo->parametro = 1;
    novo->prox = NULL;
    return(novo);
}

// Função para adicionar um nó no final da lista:
void lista_adicionar(cabeca *l, no *n)
{
    // Colocando o novo nó no final da lista:
    if(l->inicio == NULL) // Checando se a lista está vazia.
        l->inicio = n;
    else
        l->fim->prox = n;
    l->fim = n;
}

// Função auxiliar para a impressão:
int imprimir_valor(int x)
{
    printf("%d\n",x);
    return(x);
}

void *createFuncLine()
{
    // Criando uma "cabeça" de lista:
    cabeca *novo = (cabeca*)malloc(sizeof(cabeca));
    novo->inicio = NULL;
    novo->fim = NULL;
    return((void*)novo);
}

void *addFuncParam(void *funcLine, int (*f)(int, int), int param)
{
    // Alocando um novo nó:
    no *novo = no_alocar();

    // Preenchendo os dados do nó:
    novo->funcao_param = f;
    if (param != 0) novo->parametro = param;

    // Fazendo um casting de "void" para "cabeca de lista":
    cabeca *lista = (cabeca*)funcLine;

    // Colocando o novo nó no final da lista:
    lista_adicionar(lista,novo);

    // O ponteiro "novo" já é o identificador da função:
    return((void*)novo);
}

void *addFunc(void *funcLine, int (*f)(int))
{
    // Alocando um novo nó:
    no *novo = no_alocar();

    // Preenchendo os dados do nó:
    novo->funcao = f;

    // Fazendo um casting de "void" para "cabeca de lista":
    cabeca *lista = (cabeca*)funcLine;

    // Colocando o novo nó no final da lista:
    lista_adicionar(lista,novo);

    // O ponteiro "novo" já é o identificador da função:
    return((void*)novo);
}

void addPrint(void *funcLine)
{
    // Alocando um novo nó:
    no *novo = no_alocar();

    // Preenchendo os dados do nó:
    novo->funcao = &imprimir_valor;

    // Fazendo um casting de "void" para "cabeca de lista":
    cabeca *lista = (cabeca*)funcLine;

    // Colocando o novo nó no final da lista:
    lista_adicionar(lista,novo);
}

void setParam(void *op, int param)
{
    // Fazendo um casting de "void" para "no":
    no *n = (no*)op;

    // Checar se a função do nó apontado por "op" é uma função COM parâmetro:
    if(n->funcao_param != NULL)
        if (param != 0) n->parametro = param;
}

void setFunc(void *op, int (*f)(int))
{
    // Fazendo um casting de "void" para "no":
    no *n = (no*)op;

    // Checar se a função do nó apontado por "op" é uma função SEM parâmetro:
    if(n->funcao != NULL)
        n->funcao = f;
}

void runFuncLine(void *funcLine, int x)
{
    // Fazendo um casting de "void" para "cabeca de lista":
    cabeca *lista = (cabeca*)funcLine;
    no *pont = lista->inicio;

    int resultado = x;
    //int contador = 1;

    // Percorrer a lista até o final:
    while(pont != NULL)
    {
        //printf("%d) %d\n",contador,resultado);contador++;
        // Checar se a função é SEM parâmetro...
        if(pont->funcao != NULL)
            resultado = pont->funcao(resultado);
        else//... ou se é COM parâmetro:
            resultado = pont->funcao_param(resultado,pont->parametro);
        pont = pont->prox;
    }
}
